package com.yao.mybatis.executor;

import com.yao.mybatis.config.Configuration;
import com.yao.mybatis.config.MappedStatement;
import com.yao.mybatis.sqlsource.BoundSql;
import com.yao.mybatis.sqlsource.ParameterMapping;

import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Administrator
 */
public class SimpleExecutor extends BaseExecutor {
    @Override
    public List<Object> queryFromDatabase(MappedStatement mappedStatement, Configuration configuration, Object param) {
        List<Object> resultes = new ArrayList<>();
        try {
            //创建连接
            Connection connection = getConnection(configuration);
            //获取sql
            BoundSql boundSql = mappedStatement.getSqlSource().getBoundSql(param);
            //创建statement
            PreparedStatement statement = createStatement(connection, boundSql.getSqlText());
            //设置参数
            handleParameter(statement, boundSql, param);
            //执行sql
            ResultSet resultSet = statement.executeQuery();
            //封装结果
            handleResult(resultSet, mappedStatement, resultes);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultes;
    }

    /**
     * 从configuration中获取数据源信息创建连接
     *
     * @param configuration
     * @return
     * @throws SQLException
     */
    private Connection getConnection(Configuration configuration) throws SQLException {
        DataSource dataSource = configuration.getDataSource();
        Connection connection = dataSource.getConnection();
        return connection;
    }

    /**
     * 用连接创建PreparedStatement，因为需要预编译所以需要把sql传入
     *
     * @param connection
     * @param sql
     * @return
     * @throws SQLException
     */
    private PreparedStatement createStatement(Connection connection, String sql) throws SQLException {
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        return preparedStatement;
    }

    /**
     * 设置预编译参数，需要用到boundSql中参数信息
     *
     * @param statement
     * @param boundSql  boundSql中的parameterMappings是解析后的参数信息
     * @param param     实际调用传入的参数
     */
    private void handleParameter(PreparedStatement statement, BoundSql boundSql, Object param) throws SQLException, NoSuchFieldException, IllegalAccessException {
        //如果实际传入的是基本类型，就直接赋值
        if (param instanceof Integer) {
            statement.setObject(1, Integer.parseInt(param.toString()));
        } else if (param instanceof String) {
            statement.setObject(1, param.toString());
        } else {
            //说明传入的参数是对象，需要根据parameterMappings中的信息去解析
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
            if (parameterMappings != null && parameterMappings.size() > 0) {
                for (int i = 0; i < parameterMappings.size(); i++) {
                    ParameterMapping parameterMapping = parameterMappings.get(i);
                    String name = parameterMapping.getName();
                    //使用反射获取参数类型
                    Class<?> paramClass = param.getClass();
                    //通过参数名称获取对象属性
                    Field field = paramClass.getDeclaredField(name);
                    field.setAccessible(true);
                    //通过反射获取属性值并赋值sql
                    Object o = field.get(param);
                    statement.setObject(i + 1, o);

                }
            }
        }
    }

    /**
     * 处理查询结果，需要用到MappedStatement返回值信息
     *
     * @param resultSet
     * @param mappedStatement
     * @param results
     */
    private void handleResult(ResultSet resultSet, MappedStatement mappedStatement, List<Object> results) throws ClassNotFoundException, SQLException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        Class<?> aClass = Class.forName(mappedStatement.getResultType());
        while (resultSet.next()) {
            Object o = aClass.getDeclaredConstructor().newInstance();
            //获取元数据信息
            ResultSetMetaData metaData = resultSet.getMetaData();
            for (int i = 0; i < metaData.getColumnCount(); i++) {
                String columnName = metaData.getColumnName(i + 1);
                Field declaredField = aClass.getDeclaredField(columnName);
                declaredField.setAccessible(true);
                declaredField.set(o, resultSet.getObject(columnName));
            }
            results.add(o);
        }
    }
}
